/********************************************************************
 * 项目名称                ：rochoc<p>
 * 包名称                  ：com.rochoc.autoupdate<p>
 * 文件名称                ：AutoUpdateClient.java<p>
 * 编写者                 ：kfzx-luoc<p>
 * 编写日期                ：2008-12-23<p>
 * 程序功能（类）描述    ：<p>
 * 自动升级客户端对像
 * 程序变更日期            ：
 * 变更作者                ：
 * 变更说明                ：
 ********************************************************************/
package com.abigdreamer.swing.app.autoupdate;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import java.util.Properties;

/**
 * @author kfzx-luoc
 */
public class AutoUpdateClient {

	private Socket socket = null;
	private OutputStream socketOut;
	private InputStream socketIn;
	private Config config = Config.getInstance();// 配置文件对像
	private String currFileAbs = "";// 当前更新文件的全路径

	public AutoUpdateClient() {
		try {
			socket = new Socket(config.getServerIp(), Integer.parseInt(config.getServerPort()));
			socket.setSoTimeout(30000);// 30秒
		} catch (Exception e) {
			Config.print("创建与自动升级服务器之间的连接失败," + e);
			e.printStackTrace();
		}
	}

	public void update() {
		if (socket == null) {
			Config.print("无法与服务器连接，升级失败");
			return;
		}
		try {
			socketOut = socket.getOutputStream();
			socketIn = socket.getInputStream();

			// 发送本地版本信息给服务器
			socketOut.write(Config.packingCmd(AUPD.SEND_CLIENT_VERSION));// 发送传输版本信息命令
			sendClientVer();// 发送版本信息
			while (true) {

				String cmd = FileContract.unpackingCmd(socketIn);
				if (cmd == null) {
					socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
					break;
				}

				if (cmd.equals(AUPD.RECEIVED_CLIENT_VERSION)) {// 收到版本信息
					Config.print("服务器成功收到版本信息");
					socketOut.write(Config.packingCmd(AUPD.READY_TO_UPDATE));
					continue;
				} else if (cmd.equals(AUPD.SEND_FILE_ABSOULT)) {// 接收文件全路径
					Config.print("开始接收文件路径名");

					currFileAbs = receiveFileAbsPath();
					if (currFileAbs != null && !currFileAbs.equals("")) {// 成功
						socketOut.write(Config.packingCmd(AUPD.RECEIVED_FILE_ABSOULT));
						Config.print("接收文件全路径‘" + currFileAbs + "’成功");
					} else {
						Config.print("接收文件全路径失败");
						socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
						break;
					}
				} else if (cmd.equals(AUPD.START_TRANSMIT)) {// 接收文件
					if (receiveFile()) {
						socketOut.write(Config.packingCmd(AUPD.TERMINATE_TRANSMIT));
					} else {
						socketOut.write(Config.packingCmd(AUPD.BYE));
					}
				} else if (cmd.equals(AUPD.UPDATED_FAILURE)) {// 更新失败
					Config.print("版本更新失败");
					socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
					break;
				} else if (cmd.equals(AUPD.UPDATED_SUCCESSFUL)) {// 更新成功
					Config.print("版本更新成功");
					socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
					// 打开最新信息
					openFile(".\\config\\history.htm");
					break;
				} else if (cmd.equals(AUPD.NOTNEED_UPDATED)) {// 无需更新
					Config.print("已经是最新版本，无需更新");
					socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
					break;
				} else if (cmd.equals(AUPD.BYE)) {// 结束链接
					socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
					break;
				}
			}// END while(true)

			// 关闭资源及链接
			socketOut.close();
			socketIn.close();
			socket.close();
			Config.print("自动升级处理完毕");
		} catch (Exception e) {
			Config.print("升级处理失败," + e);
			e.printStackTrace();
		}
	}

	private void openFile(String file) {
		try {
			Runtime.getRuntime().exec("cmd /c " + file);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private String receiveFileAbsPath() {
		String absPath = "";
		// 接收文件全路径
		try {
			byte[] resultData = FileContract.unpacking(socketIn);

			if (resultData != null) {// 成功
				
				absPath = new String(resultData);
			} else {// 失败
				socketOut.write(Config.packingCmd(AUPD.BYE));// 结束
			}
		} catch (Exception e) {
			Config.print("接收文件全路径处理失败," + e);
		}
		return absPath;
	}

	private boolean receiveFile() {
		try {
			if (currFileAbs == null || currFileAbs.equals("")) {
				Config.print("无法获取更新文件信息，更新失败");
				return false;
			}

			// 先检查目录是否存在
			// 得到目录
			int idx = currFileAbs.lastIndexOf(File.separator);
			String path = currFileAbs.substring(0, idx);
			File file = new File(path);
			if (!file.isDirectory() || !file.exists()) {
				Config.print("新创建目录:" + path);
				file.mkdir();
			}

			byte[] resultData = FileContract.unpacking(socketIn);
			if (resultData != null) {
				file = new File(currFileAbs);
				FileOutputStream fout = new FileOutputStream(file);
				fout.write(resultData);
				fout.flush();
				fout.close();
				Config.print("成功更新文件：" + file.getAbsolutePath());
				return true;
			} else {
				Config.print("更新文件：" + file.getAbsolutePath() + "失败");
				return false;
			}
		} catch (Exception e) {
			Config.print("下载更新文件'" + currFileAbs + "'失败," + e);
			e.printStackTrace();
			return false;
		}
	}

	// 发送客户端版本信息
	private void sendClientVer() {
		try {
			File verFile = new File(Config.cfgFile);
			if (!verFile.isFile() || !verFile.exists()) {
				Config.print("版本信息文件不存在");
				return;
			}

			List<byte[]> resultData = FileContract.packing(verFile);
			for (byte[] bs : resultData) {
				socketOut.write(bs);
			}
			socketOut.flush();

			Config.print("版本信息传送完毕");
		} catch (Exception e) {
			Config.print("发送版本信息给服务器失败," + e);
			e.printStackTrace();
		}
	}

	// 测试主流程
	public static void main(String args[]) throws IOException {
		AutoUpdateClient client = new AutoUpdateClient();
		client.update();

		Properties prop = System.getProperties();
		String os = prop.getProperty("os.name");
		System.out.println(os);

		String runApp = "";
		if (os.startsWith("win") || os.startsWith("Win")) {// windows操作系统
			runApp = "runApp.bat";
		} else {
			runApp = "runApp.sh";
		}

		Process proc = Runtime.getRuntime().exec(runApp);
		String line;
		BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
		// 记录输出流
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}
		// 等待进程结束
		// proc.waitFor();
		// 进程非正常结束打印错误信息
		if (proc.exitValue() != 0) {
			br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
		}
	}
}